#include "stdafx.h"
#include <memory.h>
#include "detail/bmock_raw_len.hpp"

namespace bmock {
	raw_len::raw_len()
		:i_len_(0)
		,o_len_(0)
		,pad_len_(0)
	{}

	inline size_t calc_pad_len(const size_t t_l,const size_t,const size_t) {
		const size_t len_raw_netto = sizeof(raw_len);
		const size_t len_T_netto   = len_raw_netto / t_l;
		const size_t len_T_pad     = (len_raw_netto % t_l)?1:0;

		return len_T_netto + len_T_pad;
	}

	raw_len::raw_len(const size_t t_l,const size_t i_l,const size_t o_l)
		:i_len_(t_l*i_l)
		,o_len_(t_l*o_l)
	{
		const size_t pad_T_len = calc_pad_len(t_l,i_l,o_l);
		pad_len_ = t_l * pad_T_len;
		T_len_   = i_l + o_l + pad_T_len;
	}

	size_t raw_len::get_i_length() const {
		return i_len_;
	}

	size_t raw_len::get_o_length() const {
		return o_len_;
	}

	size_t raw_len::get_T_length() const {
		return T_len_;
	}

	size_t raw_len::get_pad_length() const {
		return pad_len_;
	}

	unsigned char *raw_len::put(unsigned char *ptr) const {
		memset(ptr,0xAB,pad_len_);
		memcpy(ptr,this,sizeof(raw_len));
		return ptr + pad_len_;
	}

	void raw_len::check_pad_length(const std::string &name,const raw_len_checker &chk) const {
		if(0==T_len_) chk.invalid_pad(name);
		const size_t t_l = (i_len_+o_len_+pad_len_)/T_len_;
		if(0==t_l) chk.invalid_pad(name);
		chk.check_pad_len(name,calc_pad_len(t_l,i_len_/t_l,o_len_/t_l),pad_len_/t_l);
	}

	const unsigned char *raw_len::get(const void *ptr,const std::string &name,const raw_len_checker &chk) {
		memcpy(this,ptr,sizeof(raw_len));
		check_pad_length(name,chk);
		return reinterpret_cast<const unsigned char *>(ptr) + pad_len_;
	}
	const unsigned char *raw_len::get_check(const void *ptr,const std::string &name,const raw_len_checker &chk){
		memcpy(this,ptr,sizeof(raw_len));
		check_not_pad_length(name,chk);
		return reinterpret_cast<const unsigned char *>(ptr) + pad_len_;
	}
	void raw_len::check_not_pad_length(const std::string &name,const raw_len_checker &chk) const{
		if(0==T_len_) chk.throw_loc();
		const size_t t_l = (i_len_+o_len_+pad_len_)/T_len_;
		if(0==t_l) chk.throw_loc();
		if(chk.check_not_pad_len(name,calc_pad_len(t_l,i_len_/t_l,o_len_/t_l),pad_len_/t_l))chk.throw_loc();
	}
}